<!DOCTYPE html>
<html>
  <head>
    <meta charset='utf-8'>

    <link rel="stylesheet" type="text/css" 
      href="/assets/css/straybirds.css" media="screen" />
    <link rel="stylesheet" type="text/css" 
      href="/assets/css/pygments.css" media="screen" />

    <!-- MathJax Section Start -->

    <script type="text/javascript"
    src="http://cdn.mathjax.org/mathjax/latest/MathJax.js?config=TeX-AMS-MML_HTMLorMML">
    </script>
    <script>
        MathJax.Hub.Config({
              tex2jax: {
              skipTags: ['script', 'noscript', 'style', 'textarea', 'pre']
              }
        });
        MathJax.Hub.Queue(function() {
            var all = MathJax.Hub.getAllJax(), i;
            for(i=0; i < all.length; i += 1) {
                all[i].SourceElement().parentNode.className += ' has-jax';
            }
        });
    </script>

    <!-- MathJax Section End -->

    <!-- Google Analytics Start-->
    <script>
  (function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
  (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
  m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
  })(window,document,'script','//www.google-analytics.com/analytics.js','ga');

  ga('create', 'UA-48100787-1', 'minixalpha.github.io');
  ga('send', 'pageview');

</script>

    <!-- Google Analytics End -->

    <title>如何编写高质量的程序</title>
  </head>

  <body>
    <div class="container">
      <header>
        <div class="container">
          <h1>
              <a href="/" title="Home Page"> 潇湘夜雨 </a>
          <span class="github-src">
            <a href ="https://github.com/minixalpha/minixalpha.github.io"
               target="_blank"
               title="Fork me on GitHub">
              <img src="/assets/images/GitHub-Mark-Light-32px.png" alt="">
            </a>
          </span>
          </h1>
        </div>
      </header>

      <aside id="left-side">
        <h2> 分类 </h2>
  <ul class="category-list">
      
            
                <li>
                <a href="/categories/计算机系统"> 计算机系统 (3) </a>
                </li>
            
      
            
                <li>
                <a href="/categories/java"> java (1) </a>
                </li>
            
      
            
                <li>
                <a href="/categories/技术"> 技术 (5) </a>
                </li>
            
      
            
                <li>
                <a href="/categories/工具"> 工具 (4) </a>
                </li>
            
      
            
                <li>
                <a href="/categories/科研"> 科研 (5) </a>
                </li>
            
      
            
                <li>
                <a href="/categories/生活"> 生活 (1) </a>
                </li>
            
      
            
                <li style="background-color: #444">
                <a href="/categories/思想"> 思想 (2) </a>
                </li>
            
      
            
                <li>
                <a href="/categories/c语言"> c语言 (4) </a>
                </li>
            
      
            
                <li>
                <a href="/categories/虚拟机"> 虚拟机 (1) </a>
                </li>
            
      
            
                <li>
                <a href="/categories/英语"> 英语 (8) </a>
                </li>
            
      
            
                <li>
                <a href="/categories/读书"> 读书 (1) </a>
                </li>
            
      
            
                <li>
                <a href="/categories/源代码阅读"> 源代码阅读 (10) </a>
                </li>
            
      
  </ul>

      </aside>

      <aside id="right-side">
        <h2> 归档 </h2>
  <ul class="archive-list">
    
    
    
        
        
        
        
            
            <li>
                <a href="/2014/05">
                    2014-05 (1)
                </a>
            </li>

        
        
    
        
        
        
        
    
        
        
        
        
    
        
        
        
        
            
            <li>
                <a href="/2014/04">
                    2014-04 (3)
                </a>
            </li>

        
        
    
        
        
        
        
    
        
        
        
        
    
        
        
        
        
    
        
        
        
        
    
        
        
        
        
    
        
        
        
        
    
        
        
        
        
    
        
        
        
        
    
        
        
        
        
    
        
        
        
        
    
        
        
        
        
            
            <li>
                <a href="/2014/03">
                    2014-03 (11)
                </a>
            </li>

        
        
    
        
        
        
        
    
        
        
        
        
    
        
        
        
        
    
        
        
        
        
    
        
        
        
        
    
        
        
        
        
            
            <li>
                <a href="/2014/02">
                    2014-02 (6)
                </a>
            </li>

        
        
    
        
        
        
        
    
        
        
        
        
    
        
        
        
        
            
            <li>
                <a href="/2014/01">
                    2014-01 (3)
                </a>
            </li>

        
        
    
        
        
        
        
    
        
        
        
        
    
        
        
        
        
    
        
        
        
        
    
        
        
        
        
    
        
        
        
        
    
        
        
        
        
    
        
        
        
        
    
        
        
        
        
    
        
        
        
        
            
            <li>
                <a href="/2013/11">
                    2013-11 (10)
                </a>
            </li>

        
        
    
        
        
        
        
    
        
        
        
        
    
        
        
        
        
            
            <li>
                <a href="/2013/10">
                    2013-10 (3)
                </a>
            </li>

        
        
    
  </ul>

      </aside>

      <article>

<h1>如何编写高质量的程序</h1>

<p>学习任何编程语言都会有一个基本的过程，开始的时候学习基本的语法，然后学习各种库，框架，开始做各种项目。在做项目的过程中，随着代码量的增加，我们会渐渐感到失去对程序的掌控能力，bug开始增加，牵一发而动全身，顾此失彼。这充分说明了编写高质量程序的重要性，这里的“高质量”主要指程序的正确性，可读性，可维护性。</p>

<h2>什么是高质量的程序</h2>

<h3>正确性</h3>

<p>程序正确性的重要程度无需多言，尤其在一些特殊领域，例如芯片制造业，航天业，武器制造业，对程序正确性往往有着极其严格的要求，因为一旦程序出错，代价往往是巨大的。在这些领域，需要使用形式化方法(formal methods)来自动验证程序的正确性，也就是说你需要证明程序的正确性，而不仅仅保证程序在大多数情况下是正确的。在其它领域，对正确性没有这么高要求，形式化方法也不适用，但是我们还是需要使用其它手段，例如测试，code review等等来保证软件的正确性。</p>

<h3>可读性</h3>

<p>可读性可以帮助程序作者理清思路，思路清晰后，程序不容易出错。另外，其它程序员在维护你的代码时，更容易理解你的意思，方便修改bug，方便扩展。</p>

<p>不要浪费自己的时间，更不要浪费别人的时间。</p>

<h3>可维护性</h3>

<p>这里的可维护性主要指程序应对变化的能力。程序在完成基本功能后，可能会发生各种改变：用户需求变了，性能达不到要求需要重新实现算法，等等。一旦程序的一个点发生改变，其它点如果也需要同时手动改变，那么程序会变的不可控制，出bug的机会会增加。想像一下，我们的程序是一个盒子，在添加新功能时，如果只需要把新模块插到一个地方，新模块就可以被系统使用，这样的程序可维护性是很高的。但是如果添加新功能时，需要把原来的程序盒子拆开，其它模块也需要相应修改，才能加入新模块，这样的程序可维护性就很差。</p>

<h2>提高程序质量的重要措施</h2>

<h3>测试</h3>

<p>为什么强调先编写测试用例，再实现程序？先编写测试用例的意义在于，让编写程序的人对程序本身有更好的理解。因为你首先得明白什么样的程序是正确的，然后才能写出正确的程序。测试用例其实是对程序正确性的一种描述。</p>

<p>为什么强调自动化测试，而不是手动测试？因为自动化测试可以增加测试的便捷度，而人们通常会更多地使用那些便捷度高的东西。我在做个人项目的时候就发现，在编写了自动测试的脚本后，我每改动一点程序，就会自动运行一下脚本，在此之前，我明知道测试很重要，但是还是不会测试的如此频繁。这样的好处是可以方便定位bug，否则在系统经过了大量改动之后，出了bug都不知道可能在哪里。</p>

<p>在对程序进行重构时，很重要的一点就在于，一定要先写好测试用例，然后每改动一点，就自动测试一下，保证程序始终保持在可控状态。</p>

<h3>良好的编程风格</h3>

<p>良好的编程风格，可以增强程序的可读性，一个结构清晰的程序，你会更容易从中发现错误。另一方面，当程序发生变化时，很可能引入新的bug，良好的编程风格可以减少这种bug的出现。下面是与编程风格相关的一些措施。</p>

<ul>
<li>风格指南</li>
</ul>

<p>找一份你使用的编程语言的风格指南，例如Google的编程语言风格指南系列，Python的PEP8，并一直遵守这份指南的内容，如果有自动化工具帮助你保持这种风格，那再好不过。</p>

<ul>
<li>最佳实践</li>
</ul>

<p>寻找你所使用语言的最佳实践，他们可读性强，经过了大量实践的考验，被广泛接受，所以尽可能多地使用他们。</p>

<ul>
<li>起一个好名字</li>
</ul>

<p>变量，函数名，类名，都需要一个好名字。程序本身是对解决方案的一种描述，一个好的名字会增强这种描述性，也会让你的思维集中于解决方案，同时让其它人更容易理解你的解决方案。</p>

<ul>
<li>不要直接使用常量</li>
</ul>

<p>在程序中直接使用的常量，一般被称为 Magic Numbers， 一方面它不利于其它程序员对程序的理解，因为没有人知道这个常量代表什么。另一方面，多个常量之间可能是有关系的，直接使用常量根本反应不出这种关系。</p>

<ul>
<li>同一变量名不要有多种含义</li>
</ul>

<p>首先这种做法降低了可读性，一个变量前面一个含义，后面一个含义，这会给阅读程序的人带来困扰。</p>

<ul>
<li>尽可能保证变量作用域小</li>
</ul>

<p>尽量减少变量定义的点与变量最后一次使用的点之间的跨度，这样可以使变量与其相关代码变得紧凑，提高可读性，不用在使用变量时再去很多的地方查看其它引用。</p>

<ul>
<li>保证函数短小精悍</li>
</ul>

<p>过长的函数会让读者陷入细节的泥潭，还需要前后来回看才能明白前面一大段和后面一大段代码的关系。将函数分解，然后给函数起一个好名字，读者马上就能明白这段代码在做什么。</p>

<h3>提高应变能力</h3>

<p>程序应对变化的能力强，可扩展性就强，也更容易在变化时保证正确性，这样的程序可维护性强。下面是一些提高程序应变能力的措施。</p>

<ul>
<li> 不要使用常量</li>
</ul>

<p>不要使用常量的另一个原因在于常量可能变化，如果程序中多次引入了这个常量，那么一旦这个常量要发生变化，就需要同时改动许多地方，这时候，如果有些地方没有改，就会使程序不一致，可能引入bug。</p>

<ul>
<li>同一变量名不要有多种含义</li>
</ul>

<p>同一变量名不要有多种含义另一个原因在于，多种含义之间可能会相互影响，第一次写程序时你可能记得这些影响，但是以后对程序进行改动的时候，你可能就忘记了。例如函数内一段代码执行后，索引<code>i</code> 的值等于一个长度，但是这段代码后，你没有将<code>i</code>赋值给另一个变量<code>len</code>，而是直接使用它。等过一段时间后，你或者其它人修改这段程序时，很可能忘了这段代码执行后<code>i</code>的值需要等于一个长度，因为这是一种隐式的约定，所以很容易被忽视。</p>

<ul>
<li>尽可能保证变量作用域小</li>
</ul>

<p>保证变量作用域小也有利于重构。当一个函数变得很长时，你可能需要将它分解成多个函数，这时候，如果变量跨度小，就可以很方便地提取函数，不用来回查找与此函数相关的变量的引用。</p>

<ul>
<li>减少代码重复</li>
</ul>

<p>如果有一段代码在很多地方重复，这就告诉你，需要把他们提取成一个函数。因为代码的重复意味着这是一块独立的逻辑，独立的逻辑可以抽象成一个函数。另一方面，一旦这段逻辑需要发生变化，只需要修改这个函数就可以了，不需要把所有地方都手动修改一遍。</p>

<ul>
<li>数据驱动</li>
</ul>

<p>数据驱动的意思是用数据表示来代替程序逻辑。例如，我们需要一个程序，判断某个月有几天，在实现时，最好用一个数组表示各个月的天数，需要哪个月直接查询就好，而不要使用大量的<code>if</code>语句来作逻辑判断。这只是一个小例子，它提醒我们，如果程序中含有大量判断语句，就应该想一想，能不能用数据来驱动逻辑，这样需要修改的时候，我们直接修改数据就好，而不用修改程序逻辑。</p>

<p>我曾经接手过一个项目，这个项目其实是一个工具集，根据用户的选择，调用不同的工具。原始的代码里，就使用了大量if语句，并且每个工具其实调用方式和代码都很相似。这样，我每次添加新工具时，就需要找到多个<code>if</code>语句块，作相应修改。如果用数据驱动的话，我们完全可以去掉这些<code>if</code>语句，在用户的选择与工具之间建立对应关系，这样每当新添加工具时，只需要把工具加到系统里，系统会根据这个表直接找到这个工具。这其实和之前举的盒子的例子很相似，添加新工具时，只需要把工具插到盒子上的槽上，根本不用打开盒子。这就大大提高了程序的可扩展性。</p>

<h3>控制复杂度</h3>

<p>要保证软件的高质量，很重要的一方面在于控制复杂度。控制复杂度的一个很重要的手段在于分解复杂的事物。我们之所以觉得一个事物复杂，是因为同一时间需要关心的事情太多，把复杂事物分解后，每次我们只需要关心很少的事情，这样就控制住了复杂度。</p>

<ul>
<li>不要使函数或类过大</li>
</ul>

<p>如果一个函数或类过大，他们会变得过分复杂，你同一时间需要关心许多细节。将函数或类变小之后，你的思维在一段时间内可以集中在同一个抽象层次，而不必过于深入其细节，这样更容易发现程序中的缺陷，因为你每次只需要关心很少的事情。在最高层，你只需要关心模块之间的关系，关心算法的流程，不必关心模块内部的事情。在最低层，你只需要关心一个模块内部的事情，而不必关心其它事情。</p>

<ul>
<li>不要使函数参数过多</li>
</ul>

<p>函数参数过多可能说明这个函数负责了太多的事情，你需要将这个函数分解。另一方面，你需要从逻辑上考虑，这些参数是不是一个整体，如果是一个整体，那么直接传过来一个结构体，或者传过来一个对象，是不是更合适？</p>

<ul>
<li>不要使抽象层次过多</li>
</ul>

<p>如果一个函数或类被分解为过多的抽象层次，在模块内部，你确实只需要关心很小的事情，但是这时候，由于模块过多，抽象层次过深，他们之间的关系又使复杂度增长起来。</p>

<h3>使用自动化工具</h3>

<p>自动化工具迫使我们养成良好的编程习惯，而且不容易出错。再次强调:</p>
<div class="highlight"><pre><code class="text language-text" data-lang="text">    工具越是使用方便，你越会频繁使用它。
</code></pre></div>
<p>所以，尽可能地让你的工具使用便捷。
例如，使用一些静态检测工具在编辑时自动帮助你检测程序的不良风格；使用重构工具帮助你重构；使用自动化测试工具在保存时自动运行测试等等。</p>

<h2>注意事项</h2>

<p>没有什么事情是一成不变的，所有的法则都需要考虑具体的情况。如果你要用一个法则，需要真正明白自己为什么要用，需要去权衡，而不要为了能用上这个法则而生搬硬套。</p>

<p>好好问问自己：</p>

<ul>
<li>变化真的存在么?</li>
<li>真的需要抽象么？</li>
<li>真的需要面向对象么？</li>
<li>真的xxx么？</li>
</ul>

<h2>参考资料</h2>

<p>这篇文章是我这段时间阅读过一些书后的想法，书目有</p>

<ul>
<li>代码大全(Code Complete)</li>
<li>重构——改善既有代码的设计(Refactoring Improving the Design of Existing Code)</li>
<li>程序设计实践(The Proactice of Programming)</li>
</ul>

<p>在阅读这些书的同时，我还在维护其它人的代码，做自己的个人项目。在阅读的过程中，我会不断地想到我做的项目哪里有问题，可以用书中提到的方法去修改，因此印象深刻。这些书单纯读也非常有好处，但是如果可以结合到自己的项目中，会有更大裨益。因为只有产生了强烈的共鸣，才能保证真正理解了一个东西。</p>

<p>上面提到的一些措施，都是我遇到过的，所以印象比较深刻，这几本书中还有大量提高程序质量的方法，我这里只是一个引子，希望给有心人打开一扇窗户。</p>


      </article>

      <div class="comments">
        
          <div id="disqus_thread"></div>
 <script type="text/javascript">
     /* * * CONFIGURATION VARIABLES: EDIT BEFORE PASTING INTO YOUR WEBPAGE * * */
     var disqus_shortname = 'minixalpha'; // required: replace example with your forum shortname

     /* * * DON'T EDIT BELOW THIS LINE * * */
     (function() {
         var dsq = document.createElement('script'); dsq.type = 'text/javascript'; dsq.async = true;
         dsq.src = '//' + disqus_shortname + '.disqus.com/embed.js';
         (document.getElementsByTagName('head')[0] || document.getElementsByTagName('body')[0]).appendChild(dsq);
     })();
 </script>
 <noscript>Please enable JavaScript to view the <a href="http://disqus.com/?ref_noscript">comments powered by Disqus.</a></noscript>
    <a href="http://disqus.com" class="dsq-brlink">comments powered by <span class="logo-disqus">Disqus</span></a>
    

        
      </div>


      <footer>
        Copyright (c) minixalpha 2014
      </footer>

    </div>
  </body>
</html>
